 /*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is OntoLing.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2004.
 * All Rights Reserved.
 *
 * OntoLing was develope
 * d by the Artificial Intelligence Research Group
 * (ai-nlp.info.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about OntoLing can be obtained at 
 * http//ai-nlp.info.uniroma2.it/software/OntoLing.html
 *
 */

 /*
  * Contributor(s): Armando Stellato stellato@info.uniroma2.it
 */

/*
 * SubclassPane.java
 *
 * Created on 14 maggio 2004, 11.33
 */

package it.uniroma2.art.ontoling.protege.ui;


import it.uniroma2.art.lw.manager.LinguisticWatermarkManager;
import it.uniroma2.art.lw.model.objects.ConceptualizedLR;
import it.uniroma2.art.lw.model.objects.LRWithGlosses;
import it.uniroma2.art.lw.model.objects.LinguisticInterface;
import it.uniroma2.art.lw.model.objects.SemIndexRetrievalException;
import it.uniroma2.art.lw.model.objects.SemanticIndex;
import it.uniroma2.art.lw.model.objects.TaxonomicalLR;
import it.uniroma2.art.ontoling.protege.OntoLingTab;
import it.uniroma2.art.ontoling.protege.util.PseudoLogger;
import it.uniroma2.art.ontoling.protege.util.Util;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTree;
import javax.swing.tree.TreePath;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.stanford.smi.protege.model.Cls;
import edu.stanford.smi.protege.model.Frame;
import edu.stanford.smi.protege.model.FrameID;
import edu.stanford.smi.protege.model.KnowledgeBase;
import edu.stanford.smi.protege.model.Model;
import edu.stanford.smi.protege.model.ModelUtilities;
import edu.stanford.smi.protege.resource.Icons;
import edu.stanford.smi.protege.resource.ResourceKey;
import edu.stanford.smi.protege.ui.ClsTreeFinder;
import edu.stanford.smi.protege.ui.ClsesTreeDragSourceListener;
import edu.stanford.smi.protege.ui.ClsesTreeTarget;
import edu.stanford.smi.protege.ui.DisplayUtilities;
import edu.stanford.smi.protege.ui.FrameRenderer;
import edu.stanford.smi.protege.ui.ParentChildRoot;
import edu.stanford.smi.protege.util.CollectionUtilities;
import edu.stanford.smi.protege.util.ComponentFactory;
import edu.stanford.smi.protege.util.ComponentUtilities;
import edu.stanford.smi.protege.util.DefaultRenderer;
import edu.stanford.smi.protege.util.LazyTreeNode;
import edu.stanford.smi.protege.util.ModalDialog;
import edu.stanford.smi.protege.util.SelectableContainer;
import edu.stanford.smi.protege.util.SelectableTree;
import edu.stanford.smi.protege.util.StandardAction;
import edu.stanford.smi.protege.util.SuperclassTraverser;
import edu.stanford.smi.protege.util.WaitCursor;

/**
 * This component displays the superclass/subclass tree on the ClsesPanel.
 * 
 * @author Ray Fergerson <fergerson@smi.stanford.edu>
 * @author Armando Stellato <stellato@info.uniroma2.it>
 */

public class SubclassPane extends SelectableContainer implements OntoLingOntoTree {
    /**
     * 
     */
    private static final long serialVersionUID = 2406464949241383211L;
    private KnowledgeBase _knowledgeBase;
    private Action _createClsAction;
    private Action _deleteClsAction;
    private SetTerminologySlotPanel _setTerminologySlotPanel;
    protected static Log logger = LogFactory.getLog(SubclassPane.class);
    private JPopupMenu ctxMenu;
    private GUI_Facade guiFacade;
    
    private final static int MAX_EXPANSIONS = 1000;

    public SubclassPane(SetTerminologySlotPanel setTerminologySlotPanel, Action doubleClickAction, Cls rootCls, Action createCls, Action deleteCls) {
        _setTerminologySlotPanel = setTerminologySlotPanel;
        _knowledgeBase = rootCls.getKnowledgeBase();
        _createClsAction = createCls;
        _deleteClsAction = deleteCls;
        guiFacade = OntoLingTab.getOntoLingTab().getGUIFacade();
        SelectableTree tree = createSelectableTree(doubleClickAction, rootCls);
        tree.setLargeModel(true);
        tree.setSelectionRow(0);
        tree.setAutoscrolls(true);
        setSelectable(tree);
        setLayout(new BorderLayout());
        JScrollPane pane = ComponentFactory.createScrollPane(tree);
        add(pane, BorderLayout.CENTER);
        add(new ClsTreeFinder(_knowledgeBase, tree), BorderLayout.SOUTH);
        setupDragAndDrop();
        getTree().setCellRenderer(FrameRenderer.createInstance());

        /*
        getTree().addMouseListener(new TreePopupMenuMouseListener(tree) {
            public JPopupMenu getPopupMenu() {
                return SubclassPane.this.getPopupMenu();
            }
        });
        */
        
        getTree().addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
            	//TODO
            	//ClsesPanel.getInstanceOfLinguisticEnricher().setCurClass((Cls)getSoleSelection());
            }
        });
    }
    
    public KnowledgeBase getKnoledgeBase(){
    	return _knowledgeBase;
    }

    protected SelectableTree createSelectableTree(Action doubleClickAction, Cls rootCls) {
        return ComponentFactory.createSelectableTree(doubleClickAction,
                new ParentChildRoot(rootCls));
    }

    private Action createCollapseAllAction() {
        return new StandardAction(ResourceKey.CLASS_BROWSER_COLLAPSE_TREE_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                ComponentUtilities.fullSelectionCollapse(getTree());
            }
        };
    }

    private Action createExpandAllAction() {
        return new StandardAction(ResourceKey.CLASS_BROWSER_EXPAND_TREE_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                ComponentUtilities.fullSelectionExpand(getTree(), MAX_EXPANSIONS);
            }
        };
    }

    public JPopupMenu getPopupMenu() {
    	return ctxMenu;
    }
    
    public void setPopupMenu(JPopupMenu menu) {
    	ctxMenu = menu;
    }
    
    public JPopupMenu createStdPopupMenu() {
        JPopupMenu menu = null;
        if (!getSelection().isEmpty()) {
            menu = new JPopupMenu();
            add(menu, _createClsAction);
            add(menu, getCreateClsWithMetaClsAction());
            add(menu, _deleteClsAction);
            addSeparator(menu);
            add(menu, getChangeMetaclassAction());
            add(menu, getChangeSubclassMetaclassAction());
            addSeparator(menu);
            add(menu, getHideClsAction());
            addSeparator(menu);
            add(menu, createSetClsMetaClsAction());
            add(menu, createSetSlotMetaClsAction());
            addSeparator(menu);
            add(menu, createExpandAllAction());
            add(menu, createCollapseAllAction());
        }
        return menu;
    }

    public Action addTerms(String label) {
        return new AbstractAction(label) {
            private static final long serialVersionUID = -5248506163594488045L;

            public void actionPerformed(ActionEvent event) {
                final Cls cls = (Cls) getSoleSelection();
                guiFacade.notifiedFrameTermInsertion((edu.stanford.smi.protege.model.Frame)cls);
            }
        };
    }

    public Action addSenseIdToResource(String label) {
        return new AbstractAction(label) {            
            private static final long serialVersionUID = 4837599759581214425L;

            public void actionPerformed(ActionEvent event) {
                final Cls cls = (Cls) getSoleSelection();
                guiFacade.notifiedFrameSenseIdInsertion((edu.stanford.smi.protege.model.Frame)cls);
            }
        };
    }    
        
    
    public Action addGloss(String label) {
        return new AbstractAction(label, Icons.getBlankIcon()) {
            private static final long serialVersionUID = 1124178798840129132L;

            public void actionPerformed(ActionEvent event) {
                final Cls cls = (Cls) getSoleSelection();
                guiFacade.notifiedFrameGlossInsertion((edu.stanford.smi.protege.model.Frame)cls);
            }
        };
    }    
    
    public Action searchLinguisticResource(String label) {
        return new AbstractAction(label, Icons.getBlankIcon()) {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent event) {
                final Cls cls = (Cls) getSoleSelection();
                String clsName = cls.getName(); //in clsName there is the complete URI for this class
                if(clsName.indexOf("#") != -1)
                	clsName = clsName.substring(clsName.indexOf("#")+1);
				else if(clsName.indexOf(":") != -1)
					clsName = clsName.substring(clsName.indexOf(":")+1);
                guiFacade.setSearchFieldText(clsName);
                guiFacade.notifiedSearchField();
            }
        };        
    }    

    public Action changeNameToSelectedTerm(String label) {
        return new AbstractAction(label, Icons.getBlankIcon()) {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent event) {
                final Cls cls = (Cls) getSoleSelection();
                String className = cls.getName();
                int lastPos = className.lastIndexOf("#"); // TODO check if this system works always
                String newClassName = className.substring(0, lastPos+1)+guiFacade.getSelectedWord();
                cls.rename(newClassName);
                //cls.rename(guiFacade.getSelectedWord());
            }
        };
    }    

    public Action createSubResourceWithSelectedTermAsID(String label) {
        return new AbstractAction(label, Icons.getBlankIcon()) {
            private static final long serialVersionUID = -9116743093752727931L;

            public void actionPerformed(ActionEvent event) {
                Collection<?> parents = getSelection();
                if (!parents.isEmpty()) {
                	Cls newClass = _knowledgeBase.createCls(null, parents);
                	String className = newClass.getName();
                    int lastPos = className.lastIndexOf("Class_");
                    String newClassName = className.substring(0, lastPos)+guiFacade.getSelectedWord();
                    newClass.rename(newClassName);
	                //extendSelection(newClass);
                }
            }
        };
    }    
    
    /*
     * LING_ENRICH
     * 
    public Action callLinguisticEnrichment(String label){
    	AbstractAction action = new AbstractAction(label,Icons.getBlankIcon()){
    		public void actionPerformed(ActionEvent event) {
    			Cls cls = (Cls) getSoleSelection();
                Jena2Protege2JenaLinguisticEnricherWrapper lenrDelegate = EnrichmentPanel.getInstanceOfLinguisticEnricherWrapper();
                if (lenrDelegate.isEnrichable((RDFResource)cls)) {
                    lenrDelegate.getLEnrDialog().getEnrichmentGUI().changeFrame((RDFResource)cls);
                    logger.debug("EnrDialog: " + lenrDelegate.getLEnrDialog());
                    logger.debug("EnrGUI: " + lenrDelegate.getLEnrDialog().getEnrichmentGUI());
                    logger.debug("\n\nHostingFrameGUI: " + lenrDelegate.getLEnrDialog().getEnrichmentGUI().getHostingFrame() + "\n\n");                
                    lenrDelegate.getLEnrDialog().getEnrichmentGUI().getHostingFrame().toFront();	
                }                
                else JOptionPane.showMessageDialog(getRootPane(), "sorry this resource has not been considered for enrichment!", "no enrichable resource", JOptionPane.INFORMATION_MESSAGE);
    		}
    	};
    	//TODO vedere bene le righe sottostanti e se danno ancora errore
    	if( EnrichmentPanel.getInstanceOfLinguisticEnricherWrapper() == null) {// da cancellare
    		logger.info("EnrichmentPanel.getInstanceOfLinguisticEnricherWrapper() == null"); // da cancellare
    		action.setEnabled(false);
    	}
    	else{
    		logger.info("EnrichmentPanel.getInstanceOfLinguisticEnricherWrapper() != null"); // da cancellare
    		action.setEnabled(EnrichmentPanel.getInstanceOfLinguisticEnricherWrapper().hasStarted());
    	}
    	return action;
    }
     */

    //TODO this one COULD be moved to DynamicMeyhodsLibrary
    //TODO take depth of taxonomy exploration (and other info) from a future preference menu
    public Action addSubResourcesUsingSubConceptsFromLinguisticResources(String label) {
        return new AbstractAction(label) {
            public void actionPerformed(ActionEvent event) {
                LinguisticInterface lint = LinguisticWatermarkManager.getSelectedInterface();
                SemanticIndex c;
                
                try {
                    c = ((ConceptualizedLR)lint).getConcept(guiFacade.getSelectedSemex());
                    Collection senses = ((TaxonomicalLR)lint).getDirectDescendants(c);
                    Collection parents = getSelection();
                    if (!parents.isEmpty()) {
                        Cls cls = null;
                        Iterator iterator = senses.iterator();
                        String[] labels;
                        //String cname;
                        while (iterator.hasNext()) {
                            c = (SemanticIndex)iterator.next();
                            labels = ((ConceptualizedLR)lint).getConceptLexicals(c);
                            cls = Util.createClassThroughLabels(_knowledgeBase, labels, parents);
                            guiFacade.notifiedFrameTermInsertion(cls, ((ConceptualizedLR)lint).getConceptLexicals(c));
                            if (lint.hasGlosses())
                                guiFacade.notifiedFrameGlossInsertion(cls, ((LRWithGlosses)lint).getConceptGloss(c));
                        }
                        //extendSelection(cls);
                    }    
                } catch (SemIndexRetrievalException e) {
                    guiFacade.showErrorMessage("lnguistic resource access error", "inconsistency between semex field and linguistic resource", e);
                }
            
            }
        };
    }         
    
    
    private Action getComparison() {
        AbstractAction action = new StandardAction(
                ResourceKey.CLASS_BROWSER_CREATE_SUBCLASS_USING_METACLASS_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                Cls rootMetaCls = _knowledgeBase.getRootClsMetaCls();
                Collection roots = CollectionUtilities.createCollection(rootMetaCls);
                Cls metaCls = pickConcreteCls(roots, "Select Metaclass");
                Collection parents = getSelection();
                if (metaCls != null && !parents.isEmpty()) {
                    Cls cls = _knowledgeBase.createCls(null, parents, metaCls);
                    extendSelection(cls);
                }
            }
        };
        boolean enabled = hasMultipleConcreteClsMetaClses();
        action.setEnabled(enabled);
        return action;
    }    
    

    private void add(JPopupMenu menu, Action action) {
        menu.add(action);
    }

    private void addSeparator(JPopupMenu menu) {
        // never add two separators in a row
        int count = menu.getComponentCount();
        Component c = menu.getComponent(count - 1);
        if (!(c instanceof JSeparator)) {
            menu.addSeparator();
        }
    }

    private Action createSetClsMetaClsAction() {
        final Cls cls = (Cls) getSoleSelection();
        AbstractAction action = new StandardAction(
                ResourceKey.CLASS_BROWSER_SET_AS_DEFAULT_METACLASS_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                _knowledgeBase.setDefaultClsMetaCls(cls);
                repaint();
            }
        };
        boolean enabled = cls != null && cls.isClsMetaCls() && !cls.isDefaultClsMetaCls()
                && cls.isConcrete();
        action.setEnabled(enabled);
        return action;
    }

    private Action createSetSlotMetaClsAction() {
        final Cls cls = (Cls) getSoleSelection();
        final boolean isDefault = cls != null && cls.isDefaultSlotMetaCls();
        ResourceKey key = isDefault ? ResourceKey.CLASS_BROWSER_UNSET_AS_DEFAULT_METASLOT_MENU_ITEM
                : ResourceKey.CLASS_BROWSER_SET_AS_DEFAULT_METASLOT_MENU_ITEM;
        AbstractAction action = new StandardAction(key) {
            public void actionPerformed(ActionEvent event) {
                _knowledgeBase.setDefaultSlotMetaCls(isDefault ? null : cls);
                repaint();
            }
        };
        boolean enabled = isDefault || (cls != null && cls.isSlotMetaCls() && cls.isConcrete());
        action.setEnabled(enabled);
        return action;
    }

    public void extendSelection(Cls cls) {
        ComponentUtilities.extendSelection(getTree(), cls);
    }

    private Action getChangeMetaclassAction() {
        Action action = new StandardAction(ResourceKey.CLASS_BROWSER_CHANGE_METACLASS_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                Collection clsMetaClses = CollectionUtilities.createCollection(_knowledgeBase
                        .getRootClsMetaCls());
                Cls metaclass = pickConcreteCls(clsMetaClses, "Select Metaclass");
                if (metaclass != null) {
                    Iterator i = getSelection().iterator();
                    while (i.hasNext()) {
                        Cls cls = (Cls) i.next();
                        if (!metaclass.equals(cls.getDirectType())) {
                            cls.setDirectType(metaclass);
                        }
                    }
                }
            }
        };
        action.setEnabled(canChangeMetaCls());
        return action;
    }

    private boolean canChangeMetaCls() {
        Cls rootMetaclass = _knowledgeBase.getRootClsMetaCls();
        final Collection c = CollectionUtilities.createCollection(rootMetaclass);
        boolean hasMultipleMetaclasses = DisplayUtilities.hasMultipleConcreteClses(_knowledgeBase,
                c);

        return hasMultipleMetaclasses && selectionIsEditable();
    }

    private boolean selectionIsEditable() {
        boolean isEditable = true;
        Iterator i = getSelection().iterator();
        while (i.hasNext() && isEditable) {
            Frame frame = (Frame) i.next();
            isEditable = frame.isEditable();
        }
        return isEditable;
    }

    protected Cls pickConcreteCls(Collection allowedClses, String text) {
        return DisplayUtilities.pickConcreteCls(this, _knowledgeBase, allowedClses, text);
    }

    private Action getChangeSubclassMetaclassAction() {
        Cls rootMetaclass = _knowledgeBase.getRootClsMetaCls();
        Collection c = CollectionUtilities.createCollection(rootMetaclass);
        boolean hasMultipleMetaclasses = DisplayUtilities.hasMultipleConcreteClses(_knowledgeBase,
                c);

        final Cls cls = (Cls) getSoleSelection();
        Action action = new StandardAction(
                ResourceKey.CLASS_BROWSER_CHANGE_METACLASS_OF_SUBCLASSES_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                Cls metaCls = cls.getDirectType();
                String text = "Change metaclass of all subclasses of ";
                text += cls.getName();
                text += " to " + metaCls.getName();
                int result = ModalDialog.showMessageDialog(SubclassPane.this, text,
                        ModalDialog.MODE_OK_CANCEL);
                if (result == ModalDialog.OPTION_OK) {
                    WaitCursor waitCursor = new WaitCursor(SubclassPane.this);
                    try {
                        cls.setDirectTypeOfSubclasses(metaCls);
                    } finally {
                        waitCursor.hide();
                    }
                }
            }
        };
        boolean enabled = cls != null && hasMultipleMetaclasses
                && cls.getDirectSubclassCount() >= 1;
        action.setEnabled(enabled);
        return action;
    }

    private Action getCreateClsWithMetaClsAction() {
        AbstractAction action = new StandardAction(
                ResourceKey.CLASS_BROWSER_CREATE_SUBCLASS_USING_METACLASS_MENU_ITEM) {
            public void actionPerformed(ActionEvent event) {
                Cls rootMetaCls = _knowledgeBase.getRootClsMetaCls();
                Collection<?> roots = CollectionUtilities.createCollection(rootMetaCls);
                Cls metaCls = pickConcreteCls(roots, "Select Metaclass");
                Collection<?> parents = getSelection();
                if (metaCls != null && !parents.isEmpty()) {
                    Cls cls = _knowledgeBase.createCls(null, parents, metaCls);
                    extendSelection(cls);
                }
            }
        };
        boolean enabled = hasMultipleConcreteClsMetaClses();
        action.setEnabled(enabled);
        return action;
    }

    public Cls getDisplayParent() {
        TreePath path = getTree().getSelectionModel().getLeadSelectionPath().getParentPath();
        LazyTreeNode node = (LazyTreeNode) path.getLastPathComponent();
        Object o = node.getUserObject();
        return (o instanceof Cls) ? (Cls) o : null;
    }

    public JComponent getDropComponent() {
        return getTree();
    }

    private Action getHideClsAction() {
        final Cls cls = (Cls) getSoleSelection();
        final boolean hide = cls == null || cls.isVisible();
        ResourceKey key = hide ? ResourceKey.CLASS_BROWSER_HIDE_CLASS_MENU_ITEM
                : ResourceKey.CLASS_BROWSER_UNHIDE_CLASS_MENU_ITEM;
        return new StandardAction(key) {
            public void actionPerformed(ActionEvent event) {
                cls.setVisible(!hide);
                repaint();
            }
        };
    }



    public JTree getTree() {
        return (JTree) getSelectable();
    }

    private boolean hasMultipleConcreteClsMetaClses() {
        // Correct but slow
        /*
         * int nConcrete = 0; Collection metaClses =
         * _knowledgeBase.getRootClsMetaCls().getSubclasses(); Iterator i =
         * metaClses.iterator(); while (i.hasNext() && nConcrete < 2) { Cls cls =
         * (Cls) i.next(); if (cls.isConcrete()) { ++nConcrete; } } return
         * nConcrete > 1;
         */

        // Wrong but fast
        Cls standardCls = _knowledgeBase.getCls(Model.Cls.STANDARD_CLASS);
        return standardCls.getDirectSubclassCount() > 0;
    }

    public void removeSelection() {
        ComponentUtilities.removeSelection(getTree());
    }

    public void setExpandedCls(Cls cls, boolean expanded) {
        Collection path = ModelUtilities.getPathToRoot(cls);
        ComponentUtilities.setExpanded(getTree(), path, expanded);
    }

    public void setFinderComponent(JComponent c) {
        add(c, BorderLayout.SOUTH);
    }

    public void setRenderer(DefaultRenderer renderer) {
        getTree().setCellRenderer(renderer);
    }

    public void setSelectedCls(Cls cls) {
        if (!getSelection().contains(cls)) {
            Collection path = ModelUtilities.getPathToRoot(cls);
            ComponentUtilities.setSelectedObjectPath(getTree(), path);
        }
    }

    public void setSelectedClses(Collection clses) {
        Collection paths = new ArrayList();
        Iterator i = clses.iterator();
        while (i.hasNext()) {
            Cls cls = (Cls) i.next();
            paths.add(ModelUtilities.getPathToRoot(cls));
        }
        ComponentUtilities.setSelectedObjectPaths(getTree(), paths);
    }

    private void setupDragAndDrop() {
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(getTree(),
                DnDConstants.ACTION_COPY_OR_MOVE, new ClsesTreeDragSourceListener());
        new DropTarget(getTree(), DnDConstants.ACTION_COPY_OR_MOVE, new ClsesTreeTarget());
    }

    public void setDisplayParent(Cls cls) {
        ComponentUtilities.setDisplayParent(getTree(), cls, new SuperclassTraverser());
    }

    public String toString() {
        return "SubclassPane";
    }
    
    public String getSelectedClass(){
    	if(getSoleSelection() != null){
    		return ((Cls)getSoleSelection()).getName();
    	}
    	return null;
    }
}